//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-04-20     ZhangYihua   first version
//
// Description  : arbiter with round-robin algorithm
//################################################################################

module arb_rr #(
parameter           PORT_NUM                = 8
) ( 
input                                       rst_n,
input                                       clk,

input               [PORT_NUM-1:0]          req,        // raw request vector
output                                      req_exist,  // logic OR of all reqs

input                                       sch_en,     // never set sch_en to 1 unless req_exist==1 
output              [PORT_NUM-1:0]          gnt,        // immediate grants according to req and gnt_pos
output              [PORT_NUM-1:0]          gnt_hld     // locked grants at sch_en==1
);

//################################################################################
// define local varialbe and localparam
//################################################################################

wire                [PORT_NUM-1:0]          mask_req;
wire                                        mask_req_exist;
wire                [PORT_NUM-1-1:0]        mask_gnt_pos;
wire                [PORT_NUM-1:0]          mask_gnt;
wire                [PORT_NUM-1-1:0]        raw_gnt_pos;
wire                [PORT_NUM-1:0]          raw_gnt;
wire                [PORT_NUM-1-1:0]        gnt_pos_c;
reg                 [PORT_NUM-1-1:0]        gnt_pos;

//################################################################################
// main
//################################################################################

// mask reqs which have been scheduled
assign mask_req = req & {gnt_pos, 1'b0}; 

arb_sp #(
        .PORT_NUM                       (PORT_NUM                       )
) u_mask_sp ( 
        .req                            (mask_req                       ),
        .req_exist                      (mask_req_exist                 ),
        .gnt_pos                        (mask_gnt_pos                   ),
        .gnt                            (mask_gnt                       )
);

arb_sp #(
        .PORT_NUM                       (PORT_NUM                       )
) u_raw_sp ( 
        .req                            (req                            ),
        .req_exist                      (req_exist                      ),
        .gnt_pos                        (raw_gnt_pos                    ),
        .gnt                            (raw_gnt                        )
);

assign gnt       = (mask_req_exist == 1'b1) ? mask_gnt     : raw_gnt;
assign gnt_pos_c = (mask_req_exist == 1'b1) ? mask_gnt_pos : raw_gnt_pos;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        gnt_pos <=`U_DLY {PORT_NUM-1{1'b0}};
    end else begin
        if (sch_en == 1'b1)
            gnt_pos <=`U_DLY gnt_pos_c;
        else
            ;
    end
end

assign gnt_hld = {1'b1, gnt_pos} ^ {gnt_pos, 1'b0};

`ifdef CBB_ASSERT_ON
// synopsys translate_off

reg     SCH_EN_CHKEN;

a_sch_when_exist: assert property (@(posedge clk) disable iff (!rst_n)
    ((SCH_EN_CHKEN!==1'b0)&sch_en |-> req_exist)
) else begin
    $error("input port 'sch_en' can't be set to 1 when 'req_exist' is 0");
end

// generally define CBB_DEBUG_ON only when debugging CBB
`ifdef CBB_DEBUG_ON

a_gnt_null: assert property (@(posedge clk) disable iff (!rst_n)
    (sch_en |-> |(req&gnt))
) else begin
    $error("reg %b by gnt %b is null", req, gnt);
    $stop;
end

a_gnt_onehot: assert property (@(posedge clk) disable iff (!rst_n)
    (sch_en |-> $onehot(gnt))
) else begin
    $error("gnt %b is not onehot", gnt);
    $stop;
end

a_gnt_hld: assert property (@(posedge clk) disable iff (!rst_n)
    (sch_en |-> ##1 (gnt_hld==$past(gnt,1)))
) else begin
    $error("gnt_hld %b not equal last gnt %b", gnt_hld, $past(gnt,1));
    $stop;
end

`endif

// synopsys translate_on
`endif

endmodule
